﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using PClusterModel.Comparer;
using PClusterModel.Infrastructure;
using System.Collections;
using PClusterModel.Utils;

namespace PClusterModel.Cluster
{
    internal class BlockPruner
    {
        private PClusterizer clusterizer;
        private int minNumberOfObjects;
        private int minNumberOfConditions;


        public BlockPruner(PClusterizer clusterizer)
        {
            this.clusterizer = clusterizer;
            this.minNumberOfConditions = this.clusterizer.MinNumberOfConditions;
            this.minNumberOfObjects = this.clusterizer.MinNumberOfObjects;
        }

        public void PruneBlocks()
        {
            bool pruned = false;
            int numberOfConditions = this.clusterizer.DataSet.NumberOfConditions;
            List<IMicroarrayDataObject> forRemoval = new List<IMicroarrayDataObject>();
            do
            {
                pruned = true;
                foreach (Block block in this.clusterizer.Blocks.Values)
                {
                    for (int i = 0; i < numberOfConditions; i++)
                    {
                        int conditionCount = block.GetMDSCountForCondition(i);
                        if (conditionCount < minNumberOfObjects - 1)
                        {
                            this.UpdateBlockEntriesForCondition(block, i);
                            if (conditionCount != 0)
                            {
                                pruned = false;
                            }
                        }
                    }
                    if (block.MaximalDimensionSets.Count < minNumberOfObjects - 1)
                    {
                        forRemoval.Add(block.Owner);
                        this.UpdateRelatedBlocksWithRemovedBlockEntries(block);
                        pruned = false;
                    }
                }
                this.RemoveBlocksForRemoval(forRemoval);
                forRemoval.Clear();
            }
            while (!pruned);
        }

        private void RemoveBlocksForRemoval(List<IMicroarrayDataObject> forRemooval)
        {
            foreach (IMicroarrayDataObject objectKey in forRemooval)
            {
                this.clusterizer.Blocks.Remove(objectKey);
            }
        }

        private void UpdateRelatedBlocksWithRemovedBlockEntries(Block block)
        {
            foreach (MaximalDimensionBlockSet mds in block.MaximalDimensionSets)
            {
                RemoveMaximalDimensionSetFromRelatedBlocks(block, mds);
            }
        }

        private void UpdateBlockEntriesForCondition(Block block, int columnIndex)
        {
            int objectCount = block.MaximalDimensionSets.Count;
            for (int i = objectCount - 1; i > -1; i--)
            {
                MaximalDimensionBlockSet mds = block.MaximalDimensionSets[i];
                for (int j = mds.ConditionBitMaps.Count - 1; j > -1; j--)
                {
                    BitArray bitmap = mds.ConditionBitMaps[j];
                    int numberOfSetBits = Utilities.GetNumberOfSetBitsForBitmap(bitmap);
                    if (numberOfSetBits < minNumberOfConditions)
                    {
                        mds.ConditionBitMaps.RemoveAt(j);
                    }
                    else
                    {
                        bitmap.Set(columnIndex, false);
                    }
                }
                if (mds.ConditionBitMaps.Count == 0)
                {
                    block.MaximalDimensionSets.RemoveAt(i);
                    RemoveMaximalDimensionSetFromRelatedBlocks(block, mds);
                }
            }
        }

        private void RemoveMaximalDimensionSetFromRelatedBlocks(Block ownerBlock, MaximalDimensionBlockSet mds)
        {
            IMicroarrayDataObject ownerOfOtherBlock = ownerBlock.Owner.Equals(mds.FirstMicroarrayObject) ? mds.SecondMicroarrayObject : mds.FirstMicroarrayObject;
            Block otherBlock = this.clusterizer.Blocks[ownerOfOtherBlock];
            otherBlock.MaximalDimensionSets.Remove(mds);
        }

    }
}
